A CLI TESTER FOR HEURISTIC CONTESTS

psytester
완전 가이드

휴리스틱 대회용 커맨드라인 테스터 psytester무엇이고, 어떤 원리로 동작하며, 어떻게 쓰는지를 데이터 흐름부터 모든 모드·옵션·내부 원리까지 도식으로 풀어냅니다.

제작: Psyho (Przemysław "Psyho" Dębiak) · github.com/FakePsyho/psytester
2025 AtCoder World Tour Finals(휴리스틱 부문) 우승 — OpenAI의 AI를 이긴 유일한 인간

스크롤하면 도식이 움직입니다
01 왜 이런 도구가 필요한가 · WHY

정답이 없는 문제를,
수백 번 재서 비교한다

Topcoder Marathon이나 AtCoder Heuristic Contest(AHC) 같은 휴리스틱 대회는 보통의 알고리즘 대회와 다릅니다.

정답이 아니라 "더 좋은 답"

대부분 NP-hard 최적화 문제입니다. 완벽한 해는 못 구하고, 남보다 조금 더 좋은 점수를 내야 이깁니다. 맞다/틀리다가 아니라 점수(score)로 겨룹니다.

시드마다 점수가 다르다

같은 코드라도 입력(seed)에 따라 점수가 들쭉날쭉. 한 케이스만 봐선 판단 불가 — 수백 개 시드에서 통계적으로 비교해야 합니다.

그래서 필요한 일

greedy, SA(담금질), beam(빔서치)… 여러 버전을 만들고, 각 버전을 같은 시드 묶음에 대해 병렬로(멀티스레드) 돌린 뒤, 점수를 한 표에 모아 비교 — 이걸 반복합니다. 손으로 하면 지옥, psytester가 자동화합니다.

한 버전을 평가하는 과정
솔루션 1개sa_v2.cpp
시드 1…100서로 다른 입력
점수 100개제각각
집계평균·최고·합
02 전체 데이터 흐름 · THE PIPELINE

코드 한 줄도 안 바꾸고,
채점기를 감싸서 점수를 긁어온다

psytester는 직접 채점하지 않습니다. 대회가 주는 공식 채점기(tester)를 대신 실행하고, 그 출력에서 정규식으로 점수를 추출해 JSON으로 저장한 뒤 표로 보여줍니다.

① 솔루션a.exe — 당신의 코드
② 테스터cmd_tester 실행
③ 출력 파일seed.out / .err
④ 정규식 추출Score= · [DATA]
⑤ 결과name.res (JSON)
⑥ 스코어보드psytester s

초록 점이 한 번의 테스트에서 데이터가 흐르는 경로입니다 — 이게 psytester r가 시드마다 반복하는 일.

② 테스터를 어떻게 실행하나

설정 파일의 cmd_tester 한 줄이 전부. 템플릿 토큰을 시드값으로 치환해 셸 명령으로 실행합니다.

# tester.cfg
cmd_tester = a.exe < in/%SEED04%.txt
> %OUTPUT_DIR%/%SEED%.out

%SEED% 시드, %SEED04% 4자리 0채움, %OUTPUT_DIR% 출력 폴더로 치환됩니다.

④ 점수는 정규식으로 긁는다

출력 파일의 모든 줄을 정규식과 대조해 점수·메타데이터를 추출합니다. 패턴은 설정에서 수정 가능.

# 채점기 출력에서
Score = 98213.0, RunTime = 1984 ms
[DATA] N = 30
# → {"score":98213.0,"time":1984,"N":30}

내 솔루션이 [DATA] 변수 = 값을 출력하면, 그 변수도 결과에 함께 저장됩니다. 이게 나중에 그룹핑의 재료가 됩니다.

⑤ 결과 파일(.res)의 모양 한 줄에 JSON 하나 — 시드 하나의 결과입니다. 버전마다 sa_v2.res, beam.res처럼 파일이 쌓이고, 스코어보드는 이 파일들을 한꺼번에 비교합니다.
sa_v2.res
{"id": 1, "score": 91002.0, "time": 1830, "N": 22}
{"id": 2, "score": 88410.0, "time": 1791, "N": 17}
{"id": 3, "score": 95044.0, "time": 1952, "N": 28}
… 시드마다 한 줄씩
03 4가지 모드 · MODES

config → run → show → find

psytester의 모든 작업은 4개의 서브커맨드로 이뤄집니다. 각각 짧은 별칭(alias)이 있습니다.

config (c)설정 만들기
run (r)테스트 실행
show (s)표로 비교
find (f)케이스 찾기
configalias: c설정 파일 만들기

대회 종류에 맞는 템플릿을 불러와 tester.cfg를 생성합니다. 한 번만 하면 됩니다.

$ psytester config --load atcoder
Creating new config file at tester.cfg
$ psytester config --list
old_topcoder Topcoder Marathon (old tester)
topcoder Topcoder Marathon; score + time
atcoder AtCoder Heuristic Contests
옵션설명
--load TEMPLATE템플릿에서 새 설정 파일 생성 (topcoder / atcoder / old_topcoder)
--save TEMPLATE현재 설정을 새 템플릿으로 저장
--delete TEMPLATE저장된 템플릿 삭제
--list사용 가능한 템플릿 목록
runalias: r테스트 배치 실행

시드 묶음을 여러 스레드로 돌리고 결과를 name.res에 씁니다. name을 안 주면 결과를 화면에 바로 출력(설정 검증용).

$ psytester r sa_v2 -t 100 -m 8 -p
Running 100 tests on 8 threads…
Progress: 100 / 100 Time: 11.804
████████████████████████████
Time: 11.804
Avg Score: 0.9621
Avg Log Scores: -0.0386
옵션설명
-t, --tests시드 개수 N(=1–N), 범위 A-B, 또는 시드 목록 파일
-m, --threads_no동시 실행 스레드 수 (설정의 threads_no 덮어씀)
-p, --progress진행 상황을 실시간 표시
-r, --retry N점수가 안 나온 테스트를 N번까지 재시도
-d, --debug왜 안 되는지 추적하는 상세 모드 (스레드 1개 강제)
name실행 이름 = 결과 파일명. 생략 시 stdout으로 출력
showalias: s스코어보드 출력

현재 폴더의 모든 .res 파일을 한 표로 비교합니다. 이 도구의 얼굴.

TestsRun 100Overall Bests Uniques Gain
greedy78.4012300.4120
sa_v191.88212243.1192
sa_v296.215361297.8014
beam94.044040145.0021
옵션설명
--groups GROUP…메타데이터별로 열을 쪼갬 (아래 04 참고)
--filters FILTER…조건에 맞는 테스트만 남김 VAR=A-B
--var VARIABLEscore 대신 다른 변수로 비교
--scoringraw(절대합) · min(낮을수록 좋음) · max(높을수록 좋음)
--sorting결과 파일 정렬: name · date
--files / --xfiles포함/제외할 결과 파일 정규식
--scale / --noscale최댓값이 scale이 되도록 정규화 / 정규화 끄기
findalias: f특정 케이스 찾기

결과를 어떤 변수 기준으로 정렬해 출력. 시간 초과 직전이거나 점수가 0(실패)인 극단 케이스를 찾을 때 유용합니다.

$ psytester f --data LATEST --var time --order - --limit 3
Finding in sa_v2.res file
{"id": 12, "score": 98213.0, "time": 1984, "N": 30}
{"id": 7, "score": 95044.0, "time": 1952, "N": 28}
{"id": 41, "score": 90112.0, "time": 1903, "N": 25}
옵션설명
--var VARIABLE정렬 기준 변수 (기본 score)
--order + / -오름차순(+) / 내림차순(-)
--limit N출력 개수 제한
--data LATEST가장 최근 결과 파일 자동 사용
활용 팁 — 부분집합 재실행 find 결과를 파일로 저장하면, 그 시드들만 다시 돌릴 수 있습니다:
psytester f … > subset && psytester r sol --tests subset
04 핵심 기능 · 그룹핑 & 필터링

"평균 점수"로는
보이지 않던 약점을 드러낸다

psytester 저자가 직접 "킬러 기능"이라 부르는 부분. 솔루션이 출력한 [DATA] 변수 = 값을 기준으로 스코어보드를 쪼개 봅니다.

재료: 솔루션이 메타데이터를 출력한다

예를 들어 문제 크기 N을 매 테스트마다 찍으면:

[DATA] N = 30 # 솔루션 코드에서 한 줄 출력

그러면 한 열이 → 여러 열로 쪼개진다

--groups N@2 는 N값 기준으로 테스트를 2개의 동일 크기 그룹으로 나눠 열을 추가합니다. 전체 평균은 비슷해도, 작은 N에선 beam이, 큰 N에선 sa_v2가 강하다 같은 사실이 드러납니다.

psytester s --groups N@2
TestsRun 100Overall 50N=10-20 50N=21-30 Bests
greedy78.401241.221037.18023
sa_v191.882146.001245.880922
sa_v296.215348.902947.312461
beam94.044046.771147.272940

초록 = 각 열의 최고 점수. 큰 N(N=21-30)에서는 beam이 역전하는 게 보입니다.

그룹 지정 문법 3가지

--groups N

N의 모든 값마다 열 1개씩 (N=10, N=11, …)

--groups N@4

N 기준 균등 크기 4개 그룹(빈)으로 자동 분할

--groups N=6-10

N이 6–10 범위인 테스트만 묶어 한 열

--filters 는 행이 아니라 "테스트 집합"을 줄인다 --filters N=5-10 은 조건에 맞는 시드만 남겨 전체 스코어보드를 그 부분집합으로 다시 계산합니다. 그룹핑(열 추가)과 필터링(테스트 제한)은 함께 쓸 수 있습니다.

집계 함수 — 점수 말고 다른 것도 본다

--varleaderboard 설정으로 임의 변수의 통계를 열로 추가할 수 있습니다.

함수의미
AVG:time평균 실행 시간
MAX:time최대(최악) 실행 시간 — 타임아웃 위험 감지
MIN / SUM최소 / 합계
GAVG:score기하평균 (0보다 큰 값만, 곱셈적 점수에 적합)
05 스코어보드 읽는 법 · READING THE BOARD

열 하나하나가
다른 질문에 답한다

무엇을 말하나
Overall전체 시드에 대한 (정규화된) 총점 — 기본 순위
GROUPS--groups로 추가된 그룹별 점수 열들
Bests이 버전이 최고 점수를 낸 테스트 수
Uniques이 버전 혼자만 최고였던 테스트 수 (공동 1위 제외)
Gain이 버전만의 기여도 = (1위 − 2위) 합 (이 버전을 지웠을 때 잃는 점수)
Fails점수가 0 이하인(실패한) 테스트 수 (0이면 자동 숨김)
Missing해당 변수 값이 없는 테스트 수

색의 의미 — 기능적이다, 장식이 아니다

■ 헤더 BRIGHT_DEFAULT — 밝은 흰색
■ 짝수 행 YELLOW — 지브라 줄무늬
■ 홀수 행 DEFAULT — 터미널 기본색
■ 그룹 최고 GREEN — 각 열 1위 강조

scoring 3가지

max높을수록 좋음 → 내 점수 / 최고
min낮을수록 좋음 → 최고 / 내 점수
raw절대 점수의 단순 합

상대 점수는 0–1로 정규화되어, --scale 100이면 최대 100점 만점으로 보입니다.

06 실제 셋업 · GET STARTED

설치하고, 첫 배치를 돌리기까지

$ pip install psytester --upgrade

Topcoder Marathon

공식 jar 테스터 다운로드
문제 폴더에서 psytester config --load topcoder (구버전 테스터면 old_topcoder)
tester.cfg에서 스레드 수, -exec를 내 실행파일명으로, scoring 방식 수정
psytester r -t 10으로 동작 확인 → 본 배치 실행

AtCoder Heuristic (AHC)

공식 tools 다운로드
문제 폴더에서 psytester config --load atcoder
스레드 수와 cmd_tester를 대회 제공 테스터에 맞게 수정 (AHC는 표준 도구가 없어 매번 다름)
테스트는 직접 생성 (psytester는 제너레이터를 쓰지 않음)
기본 워크플로우 ① 솔루션을 수동으로 한번 검증 → ② psytester r -t 10 으로 설정 확인 → ③ psytester r solA 로 배치 실행(.res 생성) → ④ psytester s 로 비교. 반복.
07 치트시트 · QUICK REFERENCE

자주 쓰는 명령

# 설정 만들기 / 목록
$ psytester c --load atcoder
$ psytester c --list
# 100개 시드, 8스레드, 진행 표시
$ psytester r sa_v2 -t 100 -m 8 -p
# 스코어보드 + N 기준 4분할 + 범위 필터
$ psytester s --groups N@4 --filters N=10-30
# 가장 느린 테스트 10개 찾기
$ psytester f --data LATEST --var time --order - --limit 10
# 그 부분집합만 다시 실행
$ psytester f … > subset && psytester r sol --tests subset
알아두면 좋은 것 • 멀티스레드로 돌리면 단일 실행보다 케이스당 속도가 느려질 수 있음(CPU 경합).
• 설정 파일은 psytester 버전과 묶임 — 버전 올리면 .cfg도 갱신해야 함(아직 하위호환 X).
• 점수 추출이 안 되면 -r(재시도) 또는 -d(디버그)로 확인.